/* 
 * $Id: pybpf.c 7557b5d4d82b 2012/12/02 09:07:16 pgurumur $ 
 *
 * Copyright (c) 2006, 2007, 2008 Prabhu Gurumurthy <pgurumur@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "pybpf.h"

static char _bpf_connect_doc[] = "Bpf connect\n";

PyTypeObject _bpf_object_type = {
   PyObject_HEAD_INIT(NULL)
   0,
   "bpf",
   sizeof(_bpf_object),
   0,

   (destructor)_bpf_object_dealloc,
   NULL,
   (getattrfunc)_bpf_getattr,
   (setattrfunc)_bpf_setattr,
   NULL,
   NULL,

   NULL,
   NULL,
   NULL,

   NULL,
   NULL,
   NULL,
   NULL,
   NULL,

   NULL,

   Py_TPFLAGS_DEFAULT,

   (char *)_bpf_connect_doc,
   (traverseproc)_bpf_traverse,

   NULL,

   NULL,

   0,

   NULL,
   NULL,

   (struct PyMethodDef *)PyBpfMethods,
   (BPF_MEMBER(*))_bpf_object_memberlist,
   NULL,
   NULL,

   NULL,
   0,
   NULL,
   0,

   NULL,
   NULL,
   NULL,
   NULL,
   NULL,
   NULL,
   NULL,
   NULL,
   0,
   0,
   0
};


DL_EXPORT(void)
init_bpf(void)
{
   PyObject *module, *dict, *version;
   module = Py_InitModule4("_bpf", PyBpfMethods, __bpf_doc, (PyObject *)NULL, 
         PYTHON_API_VERSION);
   
   if ((dict = PyModule_GetDict(module)) == NULL)
      Py_FatalError("PyModule_GetDict()");

   version = PyString_FromString(__bpf_version);
   if (version == NULL || PyDict_SetItemString(dict, __bpf_version, version)) 
      Py_FatalError("could not initialize version string");

   Py_DECREF(version);

   _bpfError = Py_BuildValue("s", "BpfError");
   if (_bpfError == NULL || PyDict_SetItemString(
            dict, "error", _bpfError))
      Py_FatalError("coult not initialize bpf Error");

   Py_DECREF(_bpfError);

   _bpf_object_type.ob_type = &PyType_Type;
   _bpf_object_type.tp_alloc = PyType_GenericAlloc;
   _bpf_object_type.tp_new = PyType_GenericNew;
   _bpf_object_type.tp_free = _PyObject_GC_Del;
   if (PyDict_SetItemString(dict, "bpfObject", (PyObject *)&_bpf_object_type))
      Py_FatalError("could not initialize bpf object type");

   Py_INCREF(&_bpf_object_type);

   fflush(stdout);
}

static void
_bpf_object_dealloc(_bpf_object *self)
{
   PyObject *obj;

   if (self->fd) {
      obj = bpf_close(self, NULL);
      Py_XDECREF(obj);
   }
}

static int
_bpf_traverse(_bpf_object *self, visitproc visit, void *arg)
{
   int ix;
   int retval = 0;

   if (self->converter) {
      if (!(ix = visit(self->converter, arg)))
         retval = ix;
   }

   return retval;
}

static PyObject *
_bpf_getattr(_bpf_object *self, char *name)
{
   PyObject *retval = NULL;

   retval = Py_FindMethod(PyBpfMethods, (PyObject *)self, name);
   if (retval == NULL) 
      PyErr_SetString(PyExc_AttributeError, name);

   return retval;
}

static int 
_bpf_setattr(_bpf_object *self, char *name, PyObject *obj)
{
   if (obj == NULL) {
      PyErr_SetString(PyExc_AttributeError, "cannot delete attributes");
      return (-1);
   }

   BPF_MEMBER(*list);
   for (list = _bpf_object_memberlist; list->name; list++)
      if (strcmp(list->name, name) == 0)
         return PyMember_SetOne((char *)self, list, obj);

   PyErr_SetString(PyExc_AttributeError, name);
   return -1;
}

static PyObject *
bpf_open(PyObject *self, PyObject *args)
{
   int ix = 0, flag = 0, immediate = 0;

   _bpf_object *_bpf = BPF_ALLOC(_bpf_object, _bpf_object_type);
   if (_bpf == NULL) {
      Py_FatalError(strerror(errno));
      exit(1);
   }
   
   if (args)
      PyArg_Parse(args, "i", &flag);

   if (flag)
      immediate = 1;

   do {
      (void)snprintf((char *)__bpf, sizeof(__bpf), "/dev/bpf%d", ix++);
      _bpf->fd = open(__bpf, O_RDWR);
   } while (_bpf->fd < 0 && errno == EBUSY);

   if (_bpf->fd < 0) 
      PyErr_SetString(_bpfError, strerror(errno));
   else {
      if (immediate) 
         if (ioctl(_bpf->fd, BIOCIMMEDIATE, &immediate) < 0) 
            PyErr_SetString(_bpfError, strerror(errno));
   }

   return (PyObject *)_bpf;
}

static PyObject *
bpf_close(_bpf_object *self, PyObject *args)
{
   PyObject *retval = NULL;

   if (self) {
      if (self->fd)
         close(self->fd);

      Py_INCREF(Py_None);
      retval = Py_None;
   }

   return retval;
}

static PyObject *
get_dl(_bpf_object *self, PyObject *args)
{
   PyObject *retval = NULL;

   int dl_value = 0;
   if (ioctl(self->fd, BIOCGDLT, (caddr_t)&dl_value) < 0)
      PyErr_SetString(_bpfError, strerror(errno));
   else 
      retval = Py_BuildValue("s", pcap_datalink_val_to_name(dl_value));

   return retval;
}

static PyObject *
get_dl_list(_bpf_object *self, PyObject *args)
{
   PyObject *dl_list = NULL;

   int max_dlt = 100;
   u_int32_t dlt_list[max_dlt];
   struct bpf_dltlist list = {max_dlt, dlt_list};
   
   if (ioctl(self->fd, BIOCGDLTLIST, &list) < 0)
      PyErr_SetString(_bpfError, strerror(errno));
   else {
      dl_list = PyList_New(0);
      int ix = 0;
      for (ix = 0; ix < list.bfl_len; ix++) 
         PyList_Append(dl_list, Py_BuildValue(
                  "s", pcap_datalink_val_to_name(list.bfl_list[ix])));
   }

   return dl_list;
}

static PyObject *
get_if(_bpf_object *self, PyObject *args)
{
   char *intf = NULL;
   char *ip = NULL;
   char *mac_addr = NULL;
   int af = AF_UNSPEC;

   if (args) 
      if (!PyArg_ParseTuple(args, "i", &af))
         af = AF_INET;

   struct ifreq *ifr = calloc(1, sizeof(struct ifreq));
   if (ifr == NULL) {
      PyErr_SetString(_bpfError, strerror(errno));
      return NULL;
   }

   if (ioctl(self->fd, BIOCGETIF, ifr) < 0)
      PyErr_SetString(_bpfError, strerror(errno));
   else {
      char buf[MAXBUF];
      memset(buf, '\0', MAXBUF);

      int sock = socket(AF_INET, SOCK_DGRAM, 0);
      if (sock < 0) {
         PyErr_SetString(_bpfError, strerror(errno));
         return NULL;
      }

      if (ioctl(sock, SIOCGIFADDR, ifr) < 0) {
         PyErr_SetString(_bpfError, strerror(errno));
         return NULL;
      }
      else {
         if (af == AF_INET) {
            struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr;
            ip = inet_ntoa(sin->sin_addr);
         }
         else if (af == AF_INET6) {
            struct sockaddr_in6 *sin = (struct sockaddr_in6 *)&ifr->ifr_addr;
            ip = (char *)inet_ntop(ifr->ifr_addr.sa_family, 
                  (void *)&(sin->sin6_addr), buf, MAXBUF);
         }
      }

      close(sock);
   
      struct ifaddrs *ifptr; 
      struct ifaddrs *ptr = NULL; 
   
      if (getifaddrs(&ifptr) < 0) { 
         PyErr_SetString(_bpfError, strerror(errno)); 
         return NULL;
      } 

      for (ptr = ifptr; ptr; ptr = ptr->ifa_next) 
         if (ptr->ifa_addr->sa_family == AF_LINK) {
            struct sockaddr_dl *dlt = (struct sockaddr_dl *)ptr->ifa_addr;
            if (strcmp(ifr->ifr_name, ptr->ifa_name) == 0)
               mac_addr = ether_ntoa((struct ether_addr *)LLADDR(dlt));
      }

      freeifaddrs(ifptr);
   }

   intf = ifr->ifr_name;
   if (ifr)
      free(ifr);

   return Py_BuildValue("(sss)", intf, ip, mac_addr);
}

static PyObject *
set_if(_bpf_object *self, PyObject *args)
{
   char *iface;
   int retval = 1;

   if (args)
      PyArg_ParseTuple(args, "s", &iface);

   if (iface) {
      struct ifreq ifr;
      strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));

      if (ioctl(self->fd, BIOCSETIF, &ifr) < 0)
         PyErr_SetString(_bpfError, strerror(errno));
      else
         retval = 0;

      self->iface = iface;
   }
   else
      PyErr_SetString(_bpfError, "no interface specified");

   return Py_BuildValue("i", retval);
}

static PyObject *
get_stats(_bpf_object *self, PyObject *args)
{
   PyObject *retval = NULL;
   struct bpf_stat stat;

   if (ioctl(self->fd, BIOCGSTATS, &stat) < 0)
      PyErr_SetString(_bpfError, strerror(errno));
   else
      retval = Py_BuildValue("(ii)", stat.bs_recv, stat.bs_drop);

   return retval;
}

static PyObject *
set_dl(_bpf_object *self, PyObject *args)
{
   int dl_type;
   int retval = 1;

   if (args)
      PyArg_ParseTuple(args, "i", &dl_type);

   if (ioctl(self->fd, BIOCSDLT, &dl_type) < 0)
      PyErr_SetString(_bpfError, strerror(errno));
   else
      retval = 0;

   return Py_BuildValue("i", retval);
}
   
static PyObject *
bpf_flush(_bpf_object *self)
{
   int retval = 1;

   if (ioctl(self->fd, BIOCFLUSH, NULL) < 0)
      PyErr_SetString(_bpfError, strerror(errno));
   else
      retval = 0;

   return Py_BuildValue("i", retval);
}

static PyObject *
set_async(_bpf_object *self, PyObject *args)
{
   int async = 0;
   int retval = 1;

   if (args)
      PyArg_ParseTuple(args, "i", &async);

   if (ioctl(self->fd, FIOASYNC, &async) < 0)
      PyErr_SetString(_bpfError, strerror(errno));
   else
      retval = 0;

   return Py_BuildValue("i", retval);
}
